﻿using Common.Logging;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Util.Props
{
    /// <summary>
    /// Some static utility methods which ease the handling of system configurations.
    /// </summary>
    public class ConfigurationManagerUtils
    {
        /// <summary>
        /// this pattern matches strings of the form '${word}' 
        /// </summary>
        private static Regex globalSymbolPattern = new Regex(@"\$\{(?<id>\w+)\}");

        /// <summary>
        /// A common property (used by all components) that sets the log level for the component.
        /// </summary>
        public static String GLOBAL_COMMON_LOGLEVEL = "logLevel";

        /// <summary>
        /// The default file suffix of configuration files.
        /// </summary>
        public static String CM_FILE_SUFFIX = ".sxl";

        /// <summary>
        /// disabled constructor because the class is just a collection of utilities for handling system configurations
        /// </summary>
        private ConfigurationManagerUtils()
        {
        }

        /// <summary>
        /// Validates that only annotated property names have been used to setup this instance of {@code
        /// util.props.ConfigurationManager}
        /// </summary>
        /// <param name="cm"></param>
        /// <returns>{@code true} if it is a valid configuration.</returns>
        public Boolean validateConfiguration(ConfigurationManager cm) 
        {
            foreach(String compName in cm.getComponentNames()) 
            {
                if (!cm.getPropertySheet(compName).Validate())
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Strips the ${ and } off of a global symbol of the form ${symbol}.
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        public static String stripGlobalSymbol(String symbol)
        {
            //MatchCollection matcher = globalSymbolPattern.Matches(symbol);
            //if (matcher.Count>0)
            //{
            //    return matcher[0].Groups[0].Value;
            //}
            //else
            //{
            //    return symbol;
            //}

            return symbol.Replace("${", string.Empty).Replace("}", String.Empty);

        }

        public static List<String> toStringList(Object obj) 
        {
            List<String> result = new List<String>();
            if (!(obj is List<Object>))
                return null;

            foreach (Object o in (List<Object>)obj) 
            {
                if (o != null) 
                {
                    result.Add(o.ToString());
                }
            }
            return result;
        }

        public static Uri getURL(String file)
        {
            try
            {
                return new Uri(file);
            }
            catch (UriFormatException e)
            {
                throw e;
            }

            return null;
        }

        /// <summary>
        /// the replacement of xml/sxl suffix is not necessary and just done to improve readability
        /// </summary>
        /// <param name="cm"></param>
        /// <returns></returns>
        public static String getLogPrefix(ConfigurationManager cm)
        {
            if (cm.getConfigURL() != null)
                return cm.getConfigURL().Replace(".sxl", "").Replace(".xml", "") + '.';
            else
                return "S4CM.";
        }

        /// <summary>
        /// Applies the system properties to the raw property map. System properties should be of the form
        /// compName[paramName]=paramValue
        /// </summary>
        /// <param name="rawMap"></param>
        /// <param name="global"></param>
        public static void applySystemProperties(Dictionary<String, RawPropertyData> rawMap, Dictionary<String, String> global)
        {

                // search for parameters of the form component[parameter]=value
                // these go in the property sheet for the component

                // look for parameters of the form foo=bar
                // these go in the global map

            IDictionary envVariables = System.Environment.GetEnvironmentVariables();
            if (envVariables != null)
            {
                foreach (DictionaryEntry propertyInfo in envVariables)
                {
                    global.Add(propertyInfo.Key.ToString(), propertyInfo.Value.ToString());
                }
            }
        //    Properties props = System.getProperties();
        //    for (Enumeration<?> e = props.keys(); e.hasMoreElements();) {
        //        String param = (String) e.nextElement();
        //        String value = props.getProperty(param);

        //        int lb = param.indexOf('[');
        //        int rb = param.indexOf(']');

        //        if (lb > 0 && rb > lb) {
        //            String compName = param.substring(0, lb);
        //            String paramName = param.substring(lb + 1, rb);
        //            RawPropertyData rpd = rawMap.get(compName);
        //            if (rpd != null) {
        //                rpd.add(paramName, value);
        //            } else {
        //                throw new InternalConfigurationException(compName, param,
        //                        "System property attempting to set parameter "
        //                                + " for unknown component " + compName
        //                                + " (" + param + ')');
        //            }
        //        }


        //        else if (param.indexOf('.') == -1) {
        //            global.put(param, value);
        //        }
        //    }
        }

        /// <summary>
        /// Configure the logger
        /// </summary>
        /// <param name="cm"></param>
        public static void configureLogger(ConfigurationManager cm)
        {
            try
            {
                Stream myFile = null;
                while (myFile == null)
                {
                    try
                    {
                        myFile = File.OpenWrite(Directory.GetCurrentDirectory() + "\\"+cm.ToString()+"_trace.Log");                     
                    }
                    catch { }
                }
                myFile.Position = myFile.Length;
                TextWriterTraceListener myTextListener = new TextWriterTraceListener(myFile);
                Trace.Listeners.Add(myTextListener);
            }
            catch (Exception)
            {

            }
            Trace.WriteLine("[" + DateTime.Now.ToString() + "]" + "Start LogSystem");
            Trace.AutoFlush = true;
            //// apply the log level (if defined) for the root logger (because we're using package based logging now)

            //String cmPrefix = getLogPrefix(cm);
            //ILog cmRootLogger = LogManager.GetLogger(cmPrefix.Substring(0, cmPrefix.Length - 1));

            //// we need to determine the root-level here, because the logManager will reset it
            //Level rootLevel = LogManager.GetLogger("");

            //configureLogger(cmRootLogger);

            //String level = cm.getGlobalProperty(GLOBAL_COMMON_LOGLEVEL);
            //if (level == null)
            //    level = Level.WARNING.getName();

            //cmRootLogger.setLevel(Level.parse(level));

            //// restore the old root logger level
            //Logger.getLogger("").setLevel(rootLevel);
        }

        public static void configureLogger(ILog logger) 
        {

            //logger.setUseParentHandlers(false);

            //Boolean hasHandler = false;

            //for (Handler handler : logger.getHandlers()) {
            //    if (handler.getFormatter() instanceof SphinxLogFormatter) {
            //        hasHandler = true;
            //        break;
            //    }
            //}

            //if (!hasHandler) {
            //    ConsoleHandler handler = new ConsoleHandler();
            //    handler.setFormatter(new SphinxLogFormatter());
            //    logger.addHandler(handler);
            //}
        }

        /**
        /// Gets a resource associated with the given parameter name given an property sheet.
         *
        /// @param name the parameter name
        /// @param ps   The property sheet which contains the property
        /// @return the resource associated with the name or NULL if it doesn't exist.
        /// @throws PropertyException if the resource cannot be found
         */
        public static String getResource(String name, PropertySheet ps, Boolean fillWithLocalPath = true)
        {
            
            String location = ps.getString(name);
            if (location == null) {
                throw new InternalConfigurationException(ps.InstanceName, name, "Required resource property '" + name + "' not set");
            }
            if (String.IsNullOrEmpty(location) && !fillWithLocalPath)
                return String.Empty;
            try {
                if (String.IsNullOrEmpty(location) && fillWithLocalPath)
                    location = Directory.GetCurrentDirectory();
                FileInfo fInfo = new FileInfo(location);

                if (fInfo == null)
                {
                    throw new InternalConfigurationException(ps.InstanceName, name, "Can't locate " + location);
                }
                return fInfo.FullName;
            } 
            catch (Exception e) {
                throw new InternalConfigurationException(e, ps.InstanceName, name, "Bad URL " + location + e.Message);
            }

        }

        //static Pattern jarPattern = Pattern.compile("resource:(.*)", Pattern.CASE_INSENSITIVE);

        //public static URL resourceToURL(String location)
        //{
        //    Matcher jarMatcher = jarPattern.matcher(location);
        //    if (jarMatcher.matches()) {
        //        String resourceName = jarMatcher.group(1);
        //        return ConfigurationManagerUtils.class.getResource(resourceName);
        //    } else {
        //        if (location.indexOf(':') == -1) {
        //            location = "file:" + location;
        //        }
        //        return new URL(location);
        //    }
        //}

        /**
       /// Why do we need this method? The reason is, that we would like to avoid this method to be part of the
       /// <code>PropertySheet</code>-API. In some circumstances it is nevertheless required to get access to the managing
       /// <code>ConfigurationManager</code>.
        */
        public static ConfigurationManager getPropertyManager(PropertySheet ps)
        {
            return ps.getPropertyManager();
        }

        /**
        /// @return <code>true</code> if <code>aClass</code> is either equal to <code>poosibleParent</code>, a subclass of
        ///         it, or implements it if <code>possibleParent</code> is an interface.
         */
        public static Boolean isDerivedClass(Type derived, Type parent)
        {
            return parent.IsAssignableFrom(derived);
        }

    }
}
